﻿#include "FITKAcousticsVTKGraphObjectSEACavity.h"
#include "FITKAcousticsVTKGraphActor.h"

// VTK
#include <vtkProperty.h>
#include <vtkUnstructuredGrid.h>
#include <vtkPoints.h>

// Data
#include "FITK_Component/FITKAcousticsModel/FITKAcousticsSEAModel.h"
#include "FITK_Component/FITKAcousticsModel/FITKAcousticsDiscNodeList.h"
#include "FITK_Component/FITKAcousticsModel/FITKSEASubSysCavity.h"
#include "FITK_Component/FITKAcousticsModel/FITKSEASubSysAbsShell.h"
#include "FITK_Component/FITKAcousticsModel/FITKAbstractSEAFace.h"
#include "FITK_Component/FITKAcousticsModel/FITKAcousticsTriangles.h"
#include "FITK_Interface/FITKInterfaceModel/FITKAbstractElement.h"

// Graph and filter
#include "FITKAcousticsVTKCommons.h"
#include "FITKAcousticsVTKSharedDataManager.h"
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor.h"

namespace Exchange
{
    FITKAcousticsVTKGraphObjectSEACavity::FITKAcousticsVTKGraphObjectSEACavity(Acoustics::FITKSEASubSysCavity* cavity)
        : FITKAcousticsVTKGraphObjectSEASubSystem(cavity)
    {
        // Return if the input is nullptr.
        if (!cavity)
        {
            return;
        }

        // Intiailize the polygon offset.
        m_polygonOffset = FITKAcousticsVTKCommons::s_polygonOffsetSEACavity;

        // Initialize vtk objects.
        init();
        
        // Generate the data set.
        if (!generateDataSet(cavity))
        {
            return;
        }

        // Generate the nodes actor.
        generateGraph();

        // Update the visibility.
        updateVisibility();

        // Save the data type.
        m_graphInfo.Type = FITKAcousticsVTKCommons::CavityGraph;
    }

    void FITKAcousticsVTKGraphObjectSEACavity::updateVisibility()
    {
        // Get the runtime settings for visibility.
        bool showSEASubSys = getGlobalSEASubSystemVisibility();
        bool showCavity = getRuntimeSettingAs<bool>(RTS_KEY_VISIBLE_SEA_SUBSYS_CAVITY, true);
        bool mainFlag = FITKAcousticsVTKGraphObject3D::getDataVisibility() && showCavity && showSEASubSys;

        // Update the visibility with the flags.
        //@{
        // Show or hide actors.
        setVisible(mainFlag);
        //@}
    }

    bool FITKAcousticsVTKGraphObjectSEACavity::generateDataSet(Acoustics::FITKAbstractSEASubSys* subSys)
    {
        Acoustics::FITKSEASubSysCavity* cavity = dynamic_cast<Acoustics::FITKSEASubSysCavity*>(subSys);
        if (!cavity || !m_points || !m_ugrid)
        {
            return false;
        }

        // Get the SEA nodes from the SEA model.
        Acoustics::FITKAcousticsDiscNodeList* seaNodes = getSEANodesData();
        if (!seaNodes)
        {
            return false;
        }

        // Generate points.
        generateSEANodes();

        // Get the face count.
        int nFaces = cavity->getFaceCount();

        double centerTemp3[3]{ 0., 0., 0. };
        for (int i = 0; i < nFaces; i++)
        {
            Acoustics::FITKAbstractSEAFace* subFace = cavity->getFace(i);
            if (!subFace)
            {
                continue;
            }

            // Get all triangles.
            Acoustics::FITKAcousticsTriangles* tris = subFace->getTriangleMesh();
            if (!tris)
            {
                return false;
            }

            double boundsFace6[6]{ 0., 0., 0., 0., 0., 0. };

            int nTris = tris->getElementCount();
            for (int j = 0; j < nTris; j++)
            {
                Interface::FITKAbstractElement* tri = tris->getElementAt(j);
                if (!tri)
                {
                    continue;
                }

                // Check the triangle.
                int nNodes = tri->getNodeCount();
                if (nNodes != 3)
                {
                    continue;
                }

                // Get the node ids.
                int nodeId_1 = tri->getNodeID(0);
                int nodeId_2 = tri->getNodeID(1);
                int nodeId_3 = tri->getNodeID(2);

                // Get the node indice.
                int ptIndex_1 = m_nodeIdPointIndexHash.value(nodeId_1, -1);
                int ptIndex_2 = m_nodeIdPointIndexHash.value(nodeId_2, -1);
                int ptIndex_3 = m_nodeIdPointIndexHash.value(nodeId_3, -1);

                if (ptIndex_1 == -1 || ptIndex_2 == -1 || ptIndex_3 == -1)
                {
                    qDebug() << QString("Invalid node id in SEA sub-systems!");
                    qDebug() << QString("Triangle node ids: %1, %2, %3.").arg(nodeId_1).arg(nodeId_2).arg(nodeId_3);
                    qDebug() << QString("Triangle node indice in VTK: %1, %2, %3.").arg(ptIndex_1).arg(ptIndex_2).arg(ptIndex_3);
                    continue;
                }

                // Add a triangle cell.
                vtkSmartPointer<vtkIdList> cell = vtkSmartPointer<vtkIdList>::New();
                cell->InsertNextId(ptIndex_1);
                cell->InsertNextId(ptIndex_2);
                cell->InsertNextId(ptIndex_3);
                m_ugrid->InsertNextCell(VTKCellType::VTK_TRIANGLE, cell);

                // Update the bounds of face.    
                updateBounds(m_points->GetPoint(ptIndex_1), boundsFace6, j == 0);
                updateBounds(m_points->GetPoint(ptIndex_2), boundsFace6, false);
                updateBounds(m_points->GetPoint(ptIndex_3), boundsFace6, false);
            }

            // Update the center.
            centerTemp3[0] += (boundsFace6[0] + boundsFace6[1]) * 0.5;
            centerTemp3[1] += (boundsFace6[2] + boundsFace6[3]) * 0.5;
            centerTemp3[2] += (boundsFace6[4] + boundsFace6[5]) * 0.5;
        }

        // Get the average center of the faces'center.
        centerTemp3[0] /= nFaces;
        centerTemp3[1] /= nFaces;
        centerTemp3[2] /= nFaces;

        // Generate the scaled points with the sub-system center.
        //double* bds = m_points->GetBounds();
        //double center3[3]{ (bds[0] + bds[1]) * 0.5,
        //    (bds[2] + bds[3]) * 0.5, (bds[4] + bds[5]) * 0.5 };
        generateScaledSEANodes(centerTemp3);

        return true;
    }

    void FITKAcousticsVTKGraphObjectSEACavity::generateGraph()
    {
        // Generate the actor.
        this->Superclass::generateGraph();

        m_fActor->setActorType(ActorType::SolidActor);
        m_fActor->GetProperty()->SetEdgeVisibility(false);

        // Initialize color.
        double color3[3]{ 0., 0., 0. };
        FITKAcousticsVTKCommons::QColorToDouble3(FITKAcousticsVTKCommons::s_SEASubSysCavityColor, color3);
        m_fActor->GetProperty()->SetColor(color3);
    }

    double FITKAcousticsVTKGraphObjectSEACavity::getScaleFactor()
    {
        return FITKAcousticsVTKCommons::s_cavityScaleFactor;
    }
}   // namespace Exchange